﻿/*Author：Lary Pan(itolmqj123@gmail.com)
 *Date  ：2016/8/11
 *Tips  ：数据管理类->所有数据的来源中心、负责同步数据库(数据库的同步策略)
 * */
using UnityEngine;
using System;
using System.Collections.Generic;
using System.Reflection;

using MiniJSON;
using DeJson;


public class DataManager
{
    private DBHelper dbHelper;

    //数据缓存
    private Dictionary<string, List<EntityBase>> dataCache;
    private List<string> changeList;

    //自增长ID
    private int incrementID = 0;

    public void Init()
    {
        dataCache = new Dictionary<string, List<EntityBase>>();
        changeList = new List<string>();
        dbHelper = new DBHelper();

        string idStr = dbHelper.Query("auto_increment_id");
        incrementID = idStr.Equals(string.Empty) ? 0 : Convert.ToInt32(idStr);
    }

    public void DoDestroy()
    {
        SyncDB();

        dataCache.Clear();
        dataCache = null;

        if (dbHelper != null)
        {
            dbHelper.CloseDB();
            dbHelper = null;
        }
    }

    #region 操作接口
    public T QueryById<T>(int id) where T : EntityBase
    {
        Type type = typeof(T);
        if (!dataCache.ContainsKey(type.Name))
        {
            _Load2Cache<T>();
        }
        return dataCache[type.Name].Find(item => item.id == id) as T;
    }

    public List<T> QueryList<T>(DBAdapter adapter = null) where T : EntityBase
    {
        Type type = typeof(T);
        if (!dataCache.ContainsKey(type.Name))
        {
            _Load2Cache<T>();
        }

        List<T> list = new List<T>();
        foreach (var item in dataCache[type.Name])
        {
            if (null == adapter) list.Add(item as T);
            else if (adapter.IsSatisfy<T>(item)) list.Add(item as T);
        }
        return list;
    }

    public void Add<T>(ref T t) where T : EntityBase
    {
        t.id = ++incrementID;
        Type type = typeof(T);
        if (!dataCache.ContainsKey(type.Name))
        {
            _Load2Cache<T>();
        }
        dataCache[type.Name].Add(t);

        if (!changeList.Contains("auto_increment_id")) changeList.Add("auto_increment_id");
        if (!changeList.Contains(type.Name)) changeList.Add(type.Name);
    }

    public void AddList<T>(ref List<T> list) where T : EntityBase
    {
        if (list.Count == 0) return;

        for (int i = 0; i < list.Count; i++)
        {
            T temp = list[i];
            this.Add<T>(ref temp);           
        }
    }


    public void DelById<T>(int id) where T : EntityBase
    {
        Type type = typeof(T);
        if (!dataCache.ContainsKey(type.Name))
        {
            _Load2Cache<T>();
        }
        dataCache[type.Name].RemoveAll(item => item.id == id);

        if (!changeList.Contains(type.Name)) changeList.Add(type.Name);
    }

    public void Del<T>(DBAdapter adapter) where T : EntityBase
    {
        Type type = typeof(T);
        if (!dataCache.ContainsKey(type.Name))
        {
            _Load2Cache<T>();
        }
        dataCache[type.Name].RemoveAll(item => adapter.IsSatisfy<T>(item));
        if (!changeList.Contains(type.Name)) changeList.Add(type.Name);
    }

    public void UpdateById<T>(int id, DBAdapter adapter) where T : EntityBase
    {
        Type type = typeof(T);
        if (!dataCache.ContainsKey(type.Name))
        {
            _Load2Cache<T>();
        }

        int count = dataCache[type.Name].Count;
        for (int i = 0; i < count; i++)
        {
            EntityBase entity = dataCache[type.Name][i];
            if (entity.id == id) adapter.UpdateValue<T>(ref entity);
        }
        if (!changeList.Contains(type.Name)) changeList.Add(type.Name);
    }

    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="adapter1">条件</param>
    /// <param name="adapter2">设值</param>
    public void Update<T>(DBAdapter adapter1, DBAdapter adapter2) where T : EntityBase
    {
        Type type = typeof(T);
        if (!dataCache.ContainsKey(type.Name))
        {
            _Load2Cache<T>();
        }

        int count = dataCache[type.Name].Count;
        for (int i = 0; i < count; i++)
        {
            EntityBase entity = dataCache[type.Name][i];
            if (adapter1.IsSatisfy<T>(entity))
                adapter2.UpdateValue<T>(ref entity);
        }
        if (!changeList.Contains(type.Name)) changeList.Add(type.Name);
    }
    #endregion

    /// <summary>
    /// 将数据库中的数据加载到缓存
    /// </summary>
    /// <param name="t"></param>
    private void _Load2Cache<T>() where T : EntityBase
    {
        Type type = typeof(T);
        dataCache.Add(type.Name, new List<EntityBase>());
        string jsonTxt = dbHelper.Query(type.Name);
        if (jsonTxt.Equals(string.Empty))  return;

        List<object> jsonData = Json.Deserialize(jsonTxt) as List<object>;
        foreach (var item in jsonData)
        {
            dataCache[type.Name].Add(_DecodeJson(type, (Dictionary<string, object>)item));
        }
    }

    /// <summary>
    /// 同步数据库
    /// </summary>
    public void SyncDB()
    {
        lock (changeList)
        {
            if (changeList.Count == 0) return;

            dbHelper.Begin();
            foreach (string key in changeList)
            {
                string value = "";
                if ("auto_increment_id".Equals(key))
                {
                    value = incrementID.ToString();
                }
                else
                {
                    value = Serializer.Serialize(dataCache[key]);
                }
                dbHelper.Update(key, value);
            }
            dbHelper.Commit();
            changeList.Clear();
        }
    }


    #region json
    /// <summary>
    /// 解析包含类型 int/float/string/int[]/float[]/string[]/bool
    /// </summary>
    /// <param name="t"></param>
    /// <param name="jsonData"></param>
    /// <returns></returns>
    private EntityBase _DecodeJson(Type t, Dictionary<string, object> jsonData)
    {
        EntityBase instance = (EntityBase)Activator.CreateInstance(t);

        FieldInfo info = null;
        foreach (var item in jsonData)
        {
            info = t.GetField(item.Key);
            if (info == null) continue;

            if (typeof(int) == info.FieldType)
            {
                info.SetValue(instance, Convert.ToInt32(item.Value));
            }
            else if (typeof(float) == info.FieldType)
            {
                info.SetValue(instance, float.Parse(item.Value.ToString()));
            }
            else if (typeof(bool) == info.FieldType)
            {
                info.SetValue(instance, Convert.ToInt32(item.Value) == 1);
            }
            else if (typeof(string) == info.FieldType)
            {
                info.SetValue(instance, item.Value.ToString());
            }
            else
            {
                List<object> list = item.Value as List<object>;
                if (list.Count == 0) continue;

                if (typeof(int[]) == info.FieldType)
                {
                    int[] arr = new int[list.Count];
                    for (int i = 0; i < arr.Length; i++)
                    {
                        arr[i] = Convert.ToInt32(list[i]);
                    }
                    info.SetValue(instance, arr);
                }
                else if (typeof(float[]) == info.FieldType)
                {
                    float[] arr = new float[list.Count];
                    for (int i = 0; i < arr.Length; i++)
                    {
                        arr[i] = Convert.ToSingle(list[i]);
                    }
                    info.SetValue(instance, arr);
                }
                else if (typeof(string[]) == info.FieldType)
                {
                    string[] arr = new string[list.Count];
                    for (int i = 0; i < arr.Length; i++)
                    {
                        arr[i] = list[i].ToString();
                    }
                    info.SetValue(instance, arr);
                }
                else if (typeof(Vector3) == info.FieldType)
                {
                    Vector3 vec = Vector3.zero;
                    if (list.Count == 3)
                    {
                        vec.x = Convert.ToSingle(list[0]);
                        vec.y = Convert.ToSingle(list[1]);
                        vec.z = Convert.ToSingle(list[2]);
                    }
                    info.SetValue(instance, vec);
                }
            }
        }
        return instance;
    }
    #endregion
}
